﻿using System;
using NUnit.Framework;
using Rhino.Mocks;
using ValidationFramework.Reflection;
using ValidationFramework.Tests.TestHelpers;

namespace ValidationFramework.Tests
{
	class ValidationPolicyTests
	{
        [TestFixture]
        public class Construction
        {
			public class TestClass
			{
				public string TestProperty { get; set; }
			}
            [Test]
            public void MemberName_Should_Match_Member_Applied_To()
            {
                // Arrange
                var typeHandle = typeof(int).TypeHandle;
				var reflectionInfo = new CachedPropertyInfo(typeof(TestClass).GetProperty("TestProperty"));

                // Act
                var policy = new ValidationPolicy(reflectionInfo);

                // Assert
                Assert.IsTrue(policy.MemberName == reflectionInfo.Name);
            }

			[Test]
			[ExpectedException(typeof(ArgumentNullException))]
			public void Should_Thow_When_Given_Null_InfoDescriptor()
			{
				// Act
				var policy = new ValidationPolicy(null);
			}

			[Test]
			public void Should_Allow_Null_Parent_And_Member_Scoped_Rules()
			{
				// Arrange
				var typeHandle = typeof(int).TypeHandle;
				var mockInfo = MockRepository.GenerateMock<ICachedReflectionInfo>();

				// Act
				var policy = new ValidationPolicy(mockInfo);

				// Assert
				Assert.AreEqual(0, policy.MemberScopeRules.Count);
				Assert.AreEqual(0, policy.ParentScopeRules.Count);
			}
        }
		[TestFixture]
		public class Validate
		{
			[Test]
			public void Should_Test_MemeberScopedRules_And_ParentParentScopedRules()
			{
				var typeHandle = typeof(int).TypeHandle;
				var mockInfo = MockRepository.GenerateMock<ICachedReflectionInfo>();

				var memberRules = new RuleCollection(typeHandle);
				memberRules.Add(new Rule(new AlwaysFailValidator()));
				memberRules.Add(new Rule(new AlwaysFailValidator()));

				var parentRules = new RuleCollection(typeHandle);
				parentRules.Add(new Rule(new AlwaysFailValidator()));
				parentRules.Add(new Rule(new AlwaysFailValidator()));

				var policy = new ValidationPolicy(mockInfo);
				policy.AddRules(memberRules, RuleScope.Member);
				policy.AddRules(parentRules, RuleScope.Parent);

				// Act
				var options = new ValidationOptions();
				var errors = policy.Validate("SomeValue", options);

				// Assert
				Assert.AreEqual(4, errors.Count);
			}

			[Test]
			[ExpectedException(typeof(ArgumentNullException))]
			public void Should_Throw_When_Validating_With_No_Options()
			{
				// Arrange
				var typeHandle = typeof(int).TypeHandle;
				var mockReflectionInfo = MockRepository.GenerateMock<ICachedReflectionInfo>();
				var policy = new ValidationPolicy(mockReflectionInfo);

				// Act
				policy.Validate(5, null);
			}

			[Test]
			public void Should_Honor_Ruleset_Option()
			{
				// Arrange
				var typeHandle = typeof(int).TypeHandle;
				var mockReflectionInfo = MockRepository.GenerateMock<ICachedReflectionInfo>();

				var parentRules = new RuleCollection(typeHandle);
				parentRules.Add(new Rule(new AlwaysFailValidator()) { RuleSet = "Test" });
				parentRules.Add(new Rule(new AlwaysFailValidator()) { RuleSet = "Test" });
				parentRules.Add(new Rule(new AlwaysFailValidator()));

				var memberRules = new RuleCollection(typeHandle);
				memberRules.Add(new Rule(new AlwaysFailValidator()) { RuleSet = "Test" });
				memberRules.Add(new Rule(new AlwaysFailValidator()));

				var policy = new ValidationPolicy(mockReflectionInfo);
				policy.AddRules(memberRules, RuleScope.Member);
				policy.AddRules(parentRules, RuleScope.Parent);

				// Act
				var options = new ValidationOptions() { RuleSet = "Test" };
				var rulesetErrors = policy.Validate("SomeValue", options);
				var allErrors = policy.Validate("SomeValue", new ValidationOptions());

				// Assert
				Assert.AreEqual(3, rulesetErrors.Count);
				Assert.AreEqual(5, allErrors.Count);
			}
			[Test]
			[ExpectedException(typeof(ValidationException))]
			public void Should_Throw_When_ThrowExceptionOnError_Option_Is_Set()
			{
				// Arrange
				var typeHandle = typeof(int).TypeHandle;
				var mockDescriptor = MockRepository.GenerateMock<ICachedReflectionInfo>();

				var parentRules = new RuleCollection(typeHandle);
				parentRules.Add(new Rule(new AlwaysFailValidator()));

				var memberRules = new RuleCollection(typeHandle);
				memberRules.Add(new Rule(new AlwaysFailValidator()) { RuleSet = "Test" });
				memberRules.Add(new Rule(new AlwaysFailValidator()));


				var policy = new ValidationPolicy(mockDescriptor);
				policy.AddRules(memberRules, RuleScope.Member);
				policy.AddRules(parentRules, RuleScope.Parent);

				// Act
				var options = new ValidationOptions() { ThrowExceptionOnError = true};
				var errors = policy.Validate("SomeValue", options);
			}
			[Test]
			[ExpectedException(typeof(ValidationException))]
			public void Should_Throw_When_ThrowOnNoRulesFound_Is_Set_And_No_Rules_Exist()
			{
				// Arrange
				var typeHandle = typeof(int).TypeHandle;
				var mockDescriptor = MockRepository.GenerateMock<ICachedReflectionInfo>();

				var policy = new ValidationPolicy(mockDescriptor);

				// Act
				var options = new ValidationOptions() { ThrowOnNoRulesFound = true };
				var errors = policy.Validate("SomeValue", options);
			}
		}
		[TestFixture]
		public class ValidateNoParent
		{
			[Test]
			public void Should_Test_Only_MemeberScopedRules()
			{
				var typeHandle = typeof(int).TypeHandle;
				var mockDescriptor = MockRepository.GenerateMock<ICachedReflectionInfo>();

				var memberRules = new RuleCollection(typeHandle);
				memberRules.Add(new Rule(new AlwaysFailValidator()));
				memberRules.Add(new Rule(new AlwaysFailValidator()));

				var parentRules = new RuleCollection(typeHandle);
				parentRules.Add(new Rule(new AlwaysFailValidator()));
				parentRules.Add(new Rule(new AlwaysFailValidator()));

				var policy = new ValidationPolicy(mockDescriptor);
				policy.AddRules(memberRules, RuleScope.Member);
				policy.AddRules(parentRules, RuleScope.Parent);

				// Act
				var options = new ValidationOptions();
				var errors = policy.ValidateNoParent("SomeValue", options);

				// Assert
				Assert.AreEqual(2, errors.Count);
			}
			[Test]
			[ExpectedException(typeof(ValidationException))]
			public void Should_Throw_When_ThrowOnNoRulesFound_Is_Set_And_No_Member_Rules_Exist()
			{
				// Arrange
				var typeHandle = typeof(int).TypeHandle;
				var mockDescriptor = MockRepository.GenerateMock<ICachedReflectionInfo>();

				var parentRules = new RuleCollection(typeHandle);
				parentRules.Add(new Rule(new AlwaysFailValidator()));

				var policy = new ValidationPolicy(mockDescriptor);
				policy.AddRules(parentRules, RuleScope.Parent);

				// Act
				var options = new ValidationOptions() { ThrowOnNoRulesFound = true };
				var errors = policy.ValidateNoParent("SomeValue", options);
			}
		}
	}
}
